﻿using System;
using System.Collections;
using System.Threading;
using System.Net;
using System.Xml;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Drawing;

namespace SudokuPanel
{


    public class ConfigInfo
    {
        private ConfigInfo() { }
        public static readonly String[] lvlList = new String[] { "容易", "普通", "困难", "极难", "骨灰" };
        /*直观法的难度感觉与逻辑解法的难度感觉是不一样的
         * 同行，同列，同九宫的唯一数是最简单的
         * 综合了行列和九宫的显式唯一数反而有点难
         * 
         * */
        private XmlDocument xmlDoc;
        private XmlNode root = null;
        private String strXmlFilename;
        private Hashtable m_props = new Hashtable();
        private Hashtable m_toplist = new Hashtable();//排行榜数据
        private static ConfigInfo m_instance = null;

        public static ConfigInfo Instance()
        {
            lock ("ConfigInfo")
            {
                if (m_instance == null) //非线程安全的实现
                {
                    m_instance = new ConfigInfo();
                    m_instance.m_props["workpath"] = System.Windows.Forms.Application.StartupPath;// System.Environment.CurrentDirectory;
                    m_instance.Load();
                    m_instance["run_count"] = (pubfun.str2int(m_instance["run_count"]) + 1).ToString();

                    m_instance.StartThread();
                }
            }
            return m_instance;
        }

        //一些对外提供的便利属性访问
        public bool hasNewVersion
        {
            get
            {
                return _hasNewVersion;
            }
            set
            {
                _hasNewVersion = value;
            }
        }

        public bool bUseKeyboard
        {
            get { return pubfun.str2int(this["use_keyboard", "0"]) > 0; }
        }
        public bool bShowHotspot
        {
            get { return pubfun.str2int(this["show_hotspot", "1"]) > 0; }
        }
        public bool bHotspotWithMouse
        {
            get { return pubfun.str2int(this["hotspot_with_mouse", "1"]) > 0; }
        }

        public void notifyWorking(bool workflag)
        {
            working = workflag;
        }

        public void notifyExit()
        {
            Save();
            if (chkThread.IsAlive)
            {
                stopThread = true;
                chkThread.Join();
            }
        }

        public Thread chkNewVersionThread;
        private volatile bool _hasNewVersion = false;
        private volatile bool _forceCheck = false;
        private void threadCheckNewVersion()
        {
            lock (chkNewVersionThread)
            {
                try
                {
                    //记录时间，避免每次启动都检查更新
                    DateTime dt = DateTime.Now;
                    DateTime lastdt;
                    try
                    {
                        lastdt = DateTime.Parse(this["last_update_time"]);
                    }
                    catch (System.Exception)
                    {
                        lastdt = dt;
                    }
                    int interval_days = pubfun.str2int(this["check_interval_days", "7"]);
                    //Debug.WriteLine(lastdt.ToString());
                    lastdt = lastdt.AddDays(interval_days);
                    //Debug.WriteLine(lastdt.ToString());
                    if (lastdt > dt && !_forceCheck)
                        return;//只有强制更新，或者已经超过更新间隔了才进行更新
                    //上次的更新依然存在
                    this["last_update_time"] = dt.ToString("yyyy-MM-dd HH:mm:ss");
                    String versioninfo = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                    if (this["new_package_version"] != "" && this["new_package_version"] != versioninfo && this["new_package_url"] != "")
                    {
                        _hasNewVersion = true;
                        return;
                    }
                    this["new_package_version"] = "";
                    this["new_package_url"] = "";
                    WebClient wc = new WebClient();
                    String strFilePackageList = m_props["workpath"] + "\\PackageList.xml";
                    Uri url = new Uri(this["packagelist", "http://klsudoku.googlecode.com/svn/trunk/csharp/PackageList.xml"]);
                    wc.DownloadFile(url, strFilePackageList);
                    if (File.Exists((strFilePackageList)))
                    {
                        XmlDocument xd = new XmlDocument();
                        xd.Load(strFilePackageList);
                        XmlElement root = (XmlElement)xd.SelectSingleNode("PackageList");
                        foreach (XmlElement xse in root.ChildNodes)
                        {
                            Debug.WriteLine(xse.Name);
                        }
                        XmlElement xe = (XmlElement)root.SelectSingleNode("last_version");
                        String last_version = xe.GetAttribute("value");
                        if (last_version != versioninfo)
                        {
                            XmlElement xeurl = (XmlElement)root.SelectSingleNode("version_" + last_version.Replace(".", "_"));
                            String lvurl = xeurl.GetAttribute("url");
                            this["new_package_url"] = lvurl;
                            this["new_package_version"] = last_version;
                            _hasNewVersion = true;
                        }

                    }
                }
                catch (System.Exception e)
                {
                    Debug.WriteLine("获取包列表错误：" + e.ToString());
                    return;
                }
            }
        }

        //后台题库生成程序
        private Thread chkThread;
        private volatile bool stopThread = false;
        public volatile bool working = false;
        protected void StartThread()
        {
            chkThread = new Thread(new ThreadStart(threadMain));
            chkThread.Start();
            checkUpdate(false);
        }

        public void checkUpdate(bool force)
        {
            lock (this)
            {
                _forceCheck = force;
                if (chkNewVersionThread != null && chkNewVersionThread.IsAlive)
                    return;
                chkNewVersionThread = new Thread(new ThreadStart(threadCheckNewVersion));
                chkNewVersionThread.Start();
            }
        }
        private void threadMain()
        {
            this["build_sudoku_library"] = this["build_sudoku_library", "1"];
            if (this["build_sudoku_library", "1"] == "0")
            {
                Debug.WriteLine("配置项[build_sudoku_library]被设置为0，不自动构建难度题库");
                stopThread = true;
                return;
            }

            int c = 0;
            while (!stopThread && c++ < 100)
                Thread.Sleep(100);//不要马上启动这个线程，避免启动时太多工作

            SudokuLibrary[] sls = new SudokuLibrary[lvlList.Length];
            for (int i = 1; i <= lvlList.Length; ++i)
            {
                String strlibfile = this["lvl_" + i + "_xml", "lvl" + i + ".xml"];
                sls[i - 1] = new SudokuLibrary(m_props["workpath"] + "\\" + strlibfile, i);
            }
            //载入各个难度库，确保每个库至少有N道题
            int n = pubfun.str2int(this["sl_num", "100"]);
            int s = pubfun.str2int(this["sl_check_interval", "10000"]);
            const int m = 50;
            int sc = s / 100;//0.1秒内响应退出
            SudokuGenerator gen = new SudokuGenerator();
            try
            {
                while (!stopThread)
                {
                    //一分钟内最多生成50题
                    //Debug.WriteLine("\r\n开始生成题目...");
                    for (int i = 0; i < sls.Length; ++i)
                    {
                        c = 0;
                        if (sls[i].Count < n)
                        {
                            while (sls[i].Count < n && c++ < m)
                            {
                                //生成题目，得到难度系数，然后放置到相应的题库里
                                if (stopThread)
                                    throw new Exception("thread has recv stop signal!");
                                Thread.Sleep(10);
                                if (!working)//只有在前台不忙的时候才工作，不然单CPU上太过影响感觉
                                    break;
                                String res = "";
                                String strSudoku = gen.do_dlx_generator(ref res);
                                puzzle pz = new puzzle();
                                pz.loadPuzzle(strSudoku.ToCharArray());
                                solver so = new solver(pz);
                                so.strResult = res;//避免遇到猜数的情况
                                so.background = true;
                                so.isSingleStep=true;//单步解题，才能将难度细致判定
                                so.do_solve();
                                int lvl = 0;
                                foreach (operation.SolveHelper helper in Enum.GetValues(typeof(operation.SolveHelper)))
                                {
                                    foreach (List<operation> oplist in so.opsteps)
                                    {
                                        foreach (operation op in oplist)
                                        {
                                            if (operation.lvl(op.helper) > lvl)
                                                lvl = operation.lvl(op.helper);
                                        }
                                    }
                                }
                                if (lvl < sls.Length)
                                    sls[(int)lvl].insertSudoku(strSudoku, true);
                                else
                                    sls[sls.Length - 1].insertSudoku(strSudoku, true);
                                if (sls[0].Count < n && lvl > 1)
                                {
                                    //为最容易的题目降级
                                    dlx_solver dso = new dlx_solver();
                                    puzzle respz = new puzzle();
                                    respz.loadPuzzle(res);
                                    while (true)
                                    {
                                        bool change = false;
                                        pz.loadPuzzle(strSudoku);
                                        foreach (List<operation> oplist in so.opsteps)
                                        {
                                            operation op = oplist[0];
                                            if (operation.lvl(op.helper) > 1)
                                            {
                                                Point p = op.m_points[0];
                                                pz[p] = respz[p];
                                                change = true;
                                            }
                                        }
                                        if (!change)//应该是知道再也没有lvl大于1的步骤（change==false）的时候，才存储
                                        {
                                            sls[0].insertSudoku(strSudoku, true);
                                            break;
                                        }
                                        strSudoku = pz.exportPuzzleCompact();
                                        if (dso.solution_count(strSudoku) != 1)
                                        {
                                            throw new Exception("has error here!");
                                        }
                                        res = dso.do_solve(strSudoku);
                                        so = new solver(pz);
                                        so.do_solve();
                                    }
                                }
                            }
                            sls[i].Save();
                            if (c > m)
                                break;//完成m题后，等下一次机会
                        }
                        if (stopThread)
                            throw new Exception("thread has recv stop signal!");
                    }
                    for (int i = 0; i < sc; ++i)
                    {
                        if (stopThread)
                            throw new Exception("thread has recv stop signal!");
                        Thread.Sleep(100);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        public String this[String index]
        {
            get
            {
                return this[index, ""];
            }
            set
            {
                this[index, ""] = value;
            }
        }

        public String this[String index, String Default]
        {
            get
            {
                lock (this)
                {
                    if (m_props.ContainsKey(index))
                        return (String)m_props[index];
                    if (Default != "")
                        m_props[index] = Default;
                    return Default;
                }
            }
            set
            {
                lock (this)
                {
                    m_props[index] = value;
                }
            }
        }
        public void Save()
        {
            saveConfigToXML();
        }
        public void Load()
        {
			strXmlFilename = (String) m_props["workpath"] ;
			strXmlFilename += System.IO.Path.DirectorySeparatorChar+"Config.xml";
            xmlDoc = new XmlDocument();
            if (File.Exists(strXmlFilename))
            {
                xmlDoc.Load(strXmlFilename);
            }
            loadConfigFromXML();
        }

        public void saveConfigToXML()
        {
            lock (this)
            {
                foreach (DictionaryEntry de in m_props)
                {
                    String key = (String)de.Key;
                    XmlNodeList xnl = xmlDoc.GetElementsByTagName(key);
                    if (xnl.Count == 0)//新增加的
                    {
                        XmlElement xe = xmlDoc.CreateElement(key);
                        root.AppendChild(xe);
                        xe.SetAttribute("value", (String)de.Value);
                    }
                    else
                    {
                        foreach (XmlElement xe in xnl)
                        {
                            if (xe.NodeType == XmlNodeType.Element)
                            {
                                xe.SetAttribute("value", (String)de.Value);
                                break;
                            }
                        }
                    }
                }
                xmlDoc.Save(strXmlFilename);
            }
        }

        //检查并设置默认值（应该先检查）
        private XmlNode initDefaultConfig()
        {
            //重新创建缺省配置
            XmlNode root = xmlDoc.CreateElement("Config");
            xmlDoc.AppendChild(root);
            //缺省难度
            XmlElement xe = xmlDoc.CreateElement("lvl");
            root.AppendChild(xe);
            xe.SetAttribute("value", "1");
            //缺省显示候选数
            xmlDoc.Save(strXmlFilename);
            //题库文件缺省名称
            for (int i = 1; i <= ConfigInfo.lvlList.Length; ++i)
            {
                String name = "lvl_" + i + "_xml";
                String value = "lvl" + i + ".xml";
                xe = xmlDoc.CreateElement(name);
                root.AppendChild(xe);
                xe.SetAttribute("value", value);
            }

            return root;
        }

        public void loadConfigFromXML()
        {
            lock (this)
            {
                try
                {
                    root = xmlDoc.SelectSingleNode("Config");//查找<Config>
                }
                catch (Exception)
                {
                }
                if (root == null)
                {
                    root = initDefaultConfig();
                    saveConfigToXML();
                }

                foreach (XmlElement xe in root.ChildNodes)
                {
                    m_props[xe.Name] = xe.GetAttribute("value");
                }
            }
        }
    }
}